package cn.edu.scau.cmi.ema.service;

import java.util.List;
import java.util.Set;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;


import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.Session;
import cn.edu.scau.cmi.ema.dao.*;
import cn.edu.scau.cmi.ema.domain.*;
import cn.edu.scau.cmi.ema.util.CmiSqlUtil;
import cn.edu.scau.cmi.ema.domain.base.CmiPagination;
import cn.edu.scau.cmi.ema.service.base.CmiEmaService;

import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import javax.persistence.Query;
import java.util.Iterator;
@Service("UserService")
@Transactional

public class UserServiceImpl extends CmiEmaService implements UserService {
	public UserServiceImpl() {
	}

	@Transactional
	public User saveUser(User user) {
		Set<Detect> existingDetectsForDetector = new HashSet<Detect>();
		Set<Event> existingEventsForAuditor = new HashSet<Event>();
		Set<Event> existingEventsForReportor = new HashSet<Event>();
		Set<Url> existingUrlsForAuthor = new HashSet<Url>();
		Set<Authority> existingAuthoritysForAuthority = new HashSet<Authority>();
		Set<Detectdepartment> existingDetectdepartmentsForDetectdepartment = new HashSet<Detectdepartment>();

		Set<Detect> selectedDetectsForDetector = user.getDetectsForDetector();
		Set<Event> selectedEventsForAuditor = user.getEventsForAuditor();
		Set<Event> selectedEventsForReportor = user.getEventsForReportor();
		Set<Url> selectedUrlsForAuthor = user.getUrlsForAuthor();
		Set<Authority> selectedAuthoritysForAuthority = user.getAuthoritysForUser();
		Set<Detectdepartment> selectedDetectdepartmentsForDetectdepartment = user.getDetectdepartmentsForUser();

		if (user.getId() == null) {

			User existingUser = userDAO.findUserByPhysicalPrimaryKey(user.getName(), user.getBirthday(),
					user.getEmail());

			if (isEquals(user, existingUser)) {
				return existingUser;
			}

			user = userDAO.store(user);
			userDAO.flush();

			for (Detect relatedDetect : selectedDetectsForDetector) {
				relatedDetect.setDetector(user);
				detectDAO.store(relatedDetect);
			}
			for (Event relatedEvent : selectedEventsForAuditor) {
				relatedEvent.setAuditor(user);
				eventDAO.store(relatedEvent);
			}
			for (Event relatedEvent : selectedEventsForReportor) {
				relatedEvent.setReportor(user);
				eventDAO.store(relatedEvent);
			}
			for (Url relatedUrl : selectedUrlsForAuthor) {
				relatedUrl.setAuthor(user);
				urlDAO.store(relatedUrl);
			}

			for (Authority relatedAuthority : selectedAuthoritysForAuthority) {
				relatedAuthority.getUsersForAuthority().add(user);
				authorityDAO.store(relatedAuthority);
			}

			for (Detectdepartment relatedDetectdepartment : selectedDetectdepartmentsForDetectdepartment) {
				relatedDetectdepartment.getUsersForDetectdepartment().add(user);
				detectdepartmentDAO.store(relatedDetectdepartment);
			}
		} else {
			User existingUser = userDAO.findUserByPrimaryKey(user.getId());

			existingDetectsForDetector = existingUser.getDetectsForDetector();
			existingEventsForAuditor = existingUser.getEventsForAuditor();
			existingEventsForReportor = existingUser.getEventsForReportor();
			existingUrlsForAuthor = existingUser.getUrlsForAuthor();
			existingAuthoritysForAuthority = existingUser.getAuthoritysForUser();
			existingDetectdepartmentsForDetectdepartment = existingUser.getDetectdepartmentsForUser();

			Set<Detect> prepareDeleteDetectsForDetector = new HashSet<Detect>();
			Set<Detect> prepareAddDetectsForDetector = new HashSet<Detect>();

			prepareDeleteDetectsForDetector.addAll(selectedDetectsForDetector);
			prepareDeleteDetectsForDetector.addAll(existingDetectsForDetector);

			prepareAddDetectsForDetector.addAll(selectedDetectsForDetector);
			prepareAddDetectsForDetector.addAll(existingDetectsForDetector);

			prepareDeleteDetectsForDetector.removeAll(selectedDetectsForDetector);
			prepareAddDetectsForDetector.removeAll(existingDetectsForDetector);

			for (Detect relatedDetect : prepareAddDetectsForDetector) {
				relatedDetect.setDetector(user);
				detectDAO.store(relatedDetect);
			}

			for (Detect relatedDetect : prepareDeleteDetectsForDetector) {
				relatedDetect.setDetector(null);
				detectDAO.store(relatedDetect);
			}
			Set<Event> prepareDeleteEventsForAuditor = new HashSet<Event>();
			Set<Event> prepareAddEventsForAuditor = new HashSet<Event>();

			prepareDeleteEventsForAuditor.addAll(selectedEventsForAuditor);
			prepareDeleteEventsForAuditor.addAll(existingEventsForAuditor);

			prepareAddEventsForAuditor.addAll(selectedEventsForAuditor);
			prepareAddEventsForAuditor.addAll(existingEventsForAuditor);

			prepareDeleteEventsForAuditor.removeAll(selectedEventsForAuditor);
			prepareAddEventsForAuditor.removeAll(existingEventsForAuditor);

			for (Event relatedEvent : prepareAddEventsForAuditor) {
				relatedEvent.setAuditor(user);
				eventDAO.store(relatedEvent);
			}

			for (Event relatedEvent : prepareDeleteEventsForAuditor) {
				relatedEvent.setAuditor(null);
				eventDAO.store(relatedEvent);
			}
			Set<Event> prepareDeleteEventsForReportor = new HashSet<Event>();
			Set<Event> prepareAddEventsForReportor = new HashSet<Event>();

			prepareDeleteEventsForReportor.addAll(selectedEventsForReportor);
			prepareDeleteEventsForReportor.addAll(existingEventsForReportor);

			prepareAddEventsForReportor.addAll(selectedEventsForReportor);
			prepareAddEventsForReportor.addAll(existingEventsForReportor);

			prepareDeleteEventsForReportor.removeAll(selectedEventsForReportor);
			prepareAddEventsForReportor.removeAll(existingEventsForReportor);

			for (Event relatedEvent : prepareAddEventsForReportor) {
				relatedEvent.setReportor(user);
				eventDAO.store(relatedEvent);
			}

			for (Event relatedEvent : prepareDeleteEventsForReportor) {
				relatedEvent.setReportor(null);
				eventDAO.store(relatedEvent);
			}
			Set<Url> prepareDeleteUrlsForAuthor = new HashSet<Url>();
			Set<Url> prepareAddUrlsForAuthor = new HashSet<Url>();

			prepareDeleteUrlsForAuthor.addAll(selectedUrlsForAuthor);
			prepareDeleteUrlsForAuthor.addAll(existingUrlsForAuthor);

			prepareAddUrlsForAuthor.addAll(selectedUrlsForAuthor);
			prepareAddUrlsForAuthor.addAll(existingUrlsForAuthor);

			prepareDeleteUrlsForAuthor.removeAll(selectedUrlsForAuthor);
			prepareAddUrlsForAuthor.removeAll(existingUrlsForAuthor);

			for (Url relatedUrl : prepareAddUrlsForAuthor) {
				relatedUrl.setAuthor(user);
				urlDAO.store(relatedUrl);
			}

			for (Url relatedUrl : prepareDeleteUrlsForAuthor) {
				relatedUrl.setAuthor(null);
				urlDAO.store(relatedUrl);
			}
			Set<Authority> prepareDeleteAuthoritysForAuthority = new HashSet<Authority>();
			Set<Authority> prepareAddAuthoritysForAuthority = new HashSet<Authority>();

			prepareDeleteAuthoritysForAuthority.addAll(selectedAuthoritysForAuthority);
			prepareDeleteAuthoritysForAuthority.addAll(existingAuthoritysForAuthority);

			prepareAddAuthoritysForAuthority.addAll(selectedAuthoritysForAuthority);
			prepareAddAuthoritysForAuthority.addAll(existingAuthoritysForAuthority);

			prepareDeleteAuthoritysForAuthority.removeAll(selectedAuthoritysForAuthority);
			prepareAddAuthoritysForAuthority.removeAll(existingAuthoritysForAuthority);

			for (Authority relatedAuthority : prepareAddAuthoritysForAuthority) {

				relatedAuthority.getUsersForAuthority().add(existingUser);
				authorityDAO.store(relatedAuthority);
			}

			for (Authority relatedAuthority : prepareDeleteAuthoritysForAuthority) {

				relatedAuthority.getUsersForAuthority().remove(existingUser);
				authorityDAO.store(relatedAuthority);
			}
			Set<Detectdepartment> prepareDeleteDetectdepartmentsForDetectdepartment = new HashSet<Detectdepartment>();
			Set<Detectdepartment> prepareAddDetectdepartmentsForDetectdepartment = new HashSet<Detectdepartment>();

			prepareDeleteDetectdepartmentsForDetectdepartment.addAll(selectedDetectdepartmentsForDetectdepartment);
			prepareDeleteDetectdepartmentsForDetectdepartment.addAll(existingDetectdepartmentsForDetectdepartment);

			prepareAddDetectdepartmentsForDetectdepartment.addAll(selectedDetectdepartmentsForDetectdepartment);
			prepareAddDetectdepartmentsForDetectdepartment.addAll(existingDetectdepartmentsForDetectdepartment);

			prepareDeleteDetectdepartmentsForDetectdepartment.removeAll(selectedDetectdepartmentsForDetectdepartment);
			prepareAddDetectdepartmentsForDetectdepartment.removeAll(existingDetectdepartmentsForDetectdepartment);

			for (Detectdepartment relatedDetectdepartment : prepareAddDetectdepartmentsForDetectdepartment) {

				relatedDetectdepartment.getUsersForDetectdepartment().add(existingUser);
				detectdepartmentDAO.store(relatedDetectdepartment);
			}

			for (Detectdepartment relatedDetectdepartment : prepareDeleteDetectdepartmentsForDetectdepartment) {

				relatedDetectdepartment.getUsersForDetectdepartment().remove(existingUser);
				detectdepartmentDAO.store(relatedDetectdepartment);
			}
			user = userDAO.store(user);
			userDAO.flush();
		}

		detectDAO.flush();
		eventDAO.flush();
		eventDAO.flush();
		urlDAO.flush();
		authorityDAO.flush();
		detectdepartmentDAO.flush();
		return user;
	}

	public void deleteUser(User user) {
		userDAO.remove(user);
		userDAO.flush();
	}

	@Transactional
	public void deleteUsers(Set<Integer> deleteUsersId) {
		Query query = userDAO.createNamedQuery("deleteUsersById", -1, -1);
		query.setParameter(1, deleteUsersId);
		query.executeUpdate();
	}

	@Transactional
	public Set<User> loadUsers() {
		return userDAO.findAllUsers();

	}

	@Transactional
	public Set<User> loadReferenceUsers(Set<User> users) {
		Iterator<User> usersIterator = users.iterator();
		Set<User> referencedUsers = new HashSet<>();
		while (usersIterator.hasNext()) {
			User currentUser = usersIterator.next();

			if (!currentUser.getDetectsForDetector().isEmpty()) {
				referencedUsers.add(currentUser);
				continue;
			}

			if (!currentUser.getEventsForAuditor().isEmpty()) {
				referencedUsers.add(currentUser);
				continue;
			}

			if (!currentUser.getEventsForReportor().isEmpty()) {
				referencedUsers.add(currentUser);
				continue;
			}

			if (!currentUser.getUrlsForAuthor().isEmpty()) {
				referencedUsers.add(currentUser);
				continue;
			}

			if (!currentUser.getAuthoritysForUser().isEmpty()) {
				referencedUsers.add(currentUser);
				continue;
			}

			if (!currentUser.getDetectdepartmentsForUser().isEmpty()) {
				referencedUsers.add(currentUser);
				continue;
			}
		}
		return referencedUsers;
	}

	public Set<User> loadUsersByIdSet(Set<Integer> userIdSet) {

		Iterator<Integer> userIdIterator = userIdSet.iterator();
		Set<User> userSet = new HashSet<>();
		while (userIdIterator.hasNext()) {
			Integer id = userIdIterator.next();
			User currentUser = userDAO.findUserById(id);
			userSet.add(currentUser);
		}
		return userSet;
	}

	@Transactional
	public List<User> findAllUsers(Integer startResult, Integer maxRows) {
		return new java.util.ArrayList<User>(userDAO.findAllUsers(startResult, maxRows));
	}

	@Transactional
	public User findUserByPrimaryKey(Integer id) {
		return userDAO.findUserByPrimaryKey(id);
	}

	@Transactional
	public Integer countUsers() {
		return ((Long) userDAO.createQuerySingleResult("select count(o) from User o").getSingleResult()).intValue();
	}

	@Transactional
	public Integer countUsers(CmiPagination page) {
		String sql = CmiSqlUtil.createCountHql(page, User.class);
		return ((Long) userDAO.createQuerySingleResult(sql).getSingleResult()).intValue();
	}

	/**
	 * search for name with ajax
	 * 
	 */
	@Transactional
	public Integer countUsersByName(String queryName) {
		Query query = userDAO.createNamedQuery("countUsersByName", -1, -1);
		query.setParameter(1, queryName);
		return new Long((long) query.getSingleResult()).intValue();
	}

	@Transactional
	public Integer countUsersByDetachedCriteria(CmiPagination page) {
		DetachedCriteria detachedCriteria = CmiSqlUtil.createDetachedCriteria(page, User.class);
		Session session = entityManager.unwrap(Session.class);
		detachedCriteria.setProjection(Projections.rowCount());
		return new Long((long) detachedCriteria.getExecutableCriteria(session).uniqueResult()).intValue();
	}

	public Map<String, ?> loadUserObjectReferencedSet(User user) {
		HashMap<String, Object> objectReferencedSetMap = new HashMap<>();

		Set<Integer> detectsIds = new HashSet<Integer>();
		for (Detect obj : user.getDetectsForDetector()) {

			detectsIds.add(obj.getId());
		}
		objectReferencedSetMap.put("detectsIds", detectsIds);
		Set<Integer> eventsIds = new HashSet<Integer>();
		for (Event event : user.getEventsForAuditor()) {
			eventsIds.add(event.getId());
		}
		objectReferencedSetMap.put("eventsIds", eventsIds);
		Set<Integer> urlsIds = new HashSet<Integer>();
		for (Url obj : user.getUrlsForAuthor()) {

			urlsIds.add(obj.getId());
		}
		objectReferencedSetMap.put("urlsIds", urlsIds);
		Set<Integer> authoritysIds = new HashSet<Integer>();
		for (Authority authority : user.getAuthoritysForUser()) {
			authoritysIds.add(authority.getId());
		}
		objectReferencedSetMap.put("authoritysIds", authoritysIds);
		Set<Integer> detectdepartmentsIds = new HashSet<Integer>();
		for (Detectdepartment detectdepartment : user.getDetectdepartmentsForUser()) {
			detectdepartmentsIds.add(detectdepartment.getId());
		}
		objectReferencedSetMap.put("detectdepartmentsIds", detectdepartmentsIds);

		return objectReferencedSetMap;
	}

	public Map<String, ?> loadUserClassReferencedSet(User user) {
		HashMap<String, Object> classReferencedSetMap = new HashMap<>();
		classReferencedSetMap.put("detects", detectDAO.findAllDetects());
		classReferencedSetMap.put("events", eventDAO.findAllEvents());
		classReferencedSetMap.put("urls", urlDAO.findAllUrls());
		classReferencedSetMap.put("authoritys", authorityDAO.findAllAuthoritys());
		classReferencedSetMap.put("detectdepartments", detectdepartmentDAO.findAllDetectdepartments());

		return classReferencedSetMap;
	}

	public boolean isEquals(User user, Object obj) {
		if (obj == user)
			return true;
		if (!(obj instanceof User))
			return false;
		User comparedUser = (User) obj;
		if (!CheckPropertyEqual(user.getName(), comparedUser.getName())) {
			return false;
		}
		if (!CheckPropertyEqual(user.getBirthday(), comparedUser.getBirthday())) {
			return false;
		}
		if (!CheckPropertyEqual(user.getEmail(), comparedUser.getEmail())) {
			return false;
		}
		return true;
	}

	public static <T> boolean CheckPropertyEqual(T originProperty, T comparedProperty) {
		// 基本类型经过参数传递后会自动装箱
		String[] className = originProperty.getClass().getName().split("\\.");
		if (className[0].equals("java") && className[1].equals("math")) {
			return ((Comparable) originProperty).compareTo(comparedProperty) == 0 ? true : false;
		} else {
			return originProperty.equals(comparedProperty);
		}
	}
}

